package com.sl.ms.carriage.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.CarriageDTO;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.domain.enums.EconomicRegionEnum;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.enums.CarriageExceptionEnum;
import com.sl.ms.carriage.mapper.CarriageMapper;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.ms.carriage.utils.CarriageUtils;
import com.sl.transport.common.exception.SLException;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String CARRIAGE_ENTITY_LIST="carriageEntityList";

    @Override
    public CarriageDTO saveOrUpdate(CarriageDTO carriageDto) {
        //1.根据条件查询模板
        //3.是否属于经济区互寄模版
        if(ObjectUtil.notEqual(carriageDto.getTemplateType(),CarriageConstant.ECONOMIC_ZONE)){
        CarriageEntity carriageEntity = this.findByTemplateType(carriageDto.getTemplateType());

        //2.判断模板是否存在，如果不存在，直接保存
        if(ObjectUtil.isEmpty(carriageEntity)){
            //直接保存
            return saveOrUpdateDTO(carriageDto);
        }

            //3.1如果不是经济区，判断id是否为空
            if(ObjectUtil.isEmpty(carriageDto.getId())){
                throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
            }
            //3.2判断当前要修改的id，已存在的模板id是否相等
            if(ObjectUtil.notEqual(carriageDto.getId(),carriageEntity.getId())){
                throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
            }
            return saveOrUpdateDTO(carriageDto);
        }
        //4.是经济区互寄模板，判断新的模板经济区和已存在的模板经济区id不能重复
        List<CarriageEntity> economicList =  findByEconomicTemplateType();

        //List<String>   [4,5]
        //economicList CarriageEntity  切割
        List<String> associatedCityList = economicList.stream()
                .map(CarriageEntity::getAssociatedCity)
                .map(associatedCityStr -> StrUtil.split(associatedCityStr, ","))
                .flatMap(StreamUtil::of)
                .collect(Collectors.toList());

        Collection<String> intersection = CollUtil.intersection(associatedCityList, carriageDto.getAssociatedCityList());
        if(CollUtil.isNotEmpty(intersection)){
            throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT);
        }

        return saveOrUpdateDTO(carriageDto);
    }

    @NotNull
    private CarriageDTO saveOrUpdateDTO(CarriageDTO carriageDto) {
        CarriageEntity entity = CarriageUtils.toEntity(carriageDto);
        this.saveOrUpdate(entity);
        stringRedisTemplate.delete(CARRIAGE_ENTITY_LIST);
        return CarriageUtils.toDTO(entity);
    }

    @Override
    public List<CarriageDTO> findAll() {
        //从redis里面获取键
        String redisKey = stringRedisTemplate.opsForValue().get(CARRIAGE_ENTITY_LIST);
        //建一个list集合用来存储list
        List<CarriageEntity> carriageEntityList=null;
        //判断redis缓存是否为空
        if(ObjectUtil.isEmpty(redisKey)) {
            //若为空，从数据库根据时间降序排序查询
            LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
                    .orderByDesc(CarriageEntity::getCreated);
            carriageEntityList = this.list(queryWrapper);
            //存缓存
            stringRedisTemplate.opsForValue().set(CARRIAGE_ENTITY_LIST,JSONUtil.toJsonStr(carriageEntityList));
        }else {
            //转换为JSON
            carriageEntityList=JSONUtil.toList(redisKey,CarriageEntity.class);
        }
        //将entity转换为DTO
        return carriageEntityList.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());
    }


    @Override
    public CarriageDTO compute(WaybillDTO waybillDTO) {
        //1.查询运费模板
        CarriageEntity carriageEntity = findCarriageTemplate(waybillDTO);
        //2.算出计费重量
       double weight= computeWeight(waybillDTO,carriageEntity);
        //3。根据公式计算运费
        //运费=首重价格+继重价格*（计费重量-1）
        double expense = carriageEntity.getFirstWeight() + carriageEntity.getContinuousWeight() * (weight - 1);
        expense = NumberUtil.round(expense, 1).doubleValue();
        CarriageDTO carriageDTO = CarriageUtils.toDTO(carriageEntity);
        carriageDTO.setExpense(expense);
        return carriageDTO;

    }

    private double computeWeight(WaybillDTO waybillDTO,CarriageEntity carriageEntity) {
        //1.查询体积
        Integer volume = waybillDTO.getVolume();
        if(ObjectUtil.isEmpty(volume)){
            Integer measureLong = waybillDTO.getMeasureLong();
            Integer measureWidth = waybillDTO.getMeasureWidth();
            Integer measureHigh = waybillDTO.getMeasureHigh();
            try {
                volume = measureLong * measureWidth * measureHigh;
            } catch (Exception e) {
              volume=0;
            }
        }
        //2.体积重量=体积/轻抛系数
        BigDecimal volumeWeight = NumberUtil.div(volume, carriageEntity.getLightThrowingCoefficient());

        //3.对比实际重量和体积重量  选最大值
        double computeWeight = NumberUtil.max(volumeWeight.doubleValue(), waybillDTO.getWeight());

        //4.按照需求优化小数点
        //不满1kg，按1kg计算
        if(computeWeight<1){
            return 1;
        }
        //10KG以下：以0.1kg为计重单位，，四舍五入保留1位小数
        if(computeWeight<10){
            return NumberUtil.round(computeWeight,1).doubleValue();
        }
        //100kg及其以上：四舍五入取整
        if(computeWeight>=100){
            return NumberUtil.round(computeWeight,0).doubleValue();

        }
        //10-100kg：继重以0.5kg为计重单位，不足0.5kg按0.5kg计算，四舍五入保留1位小数
         //18.1  按18.5计算  18
        //18.6   按19计算
        double roud = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).doubleValue();//向下取整
        double sub = computeWeight - roud;
        if(sub<0.5){
            return NumberUtil.add(roud,0.5);
        }else {
            return NumberUtil.add(roud,1);
        }

    }

    public  List<CarriageEntity> findByEconomicTemplateType(){
       LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
               .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE);
     return this.list(queryWrapper);

   }
    @Resource
    private AreaFeign areaFeign;

   private CarriageEntity findCarriageTemplate(WaybillDTO waybillDTO){
        //着模板的顺序：同城--》同省--》经济区--》跨省

       //1.找同城模板
       Long senderCityId = waybillDTO.getSenderCityId();
       Long receiverCityId = waybillDTO.getReceiverCityId();
       if(ObjectUtil.equals(senderCityId,receiverCityId)){
           CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_CITY);
           if(ObjectUtil.isNotEmpty(carriageEntity)){
               return carriageEntity;
           }
       }
       //2.找同省模板
       Long sendProvinceId = areaFeign.get(senderCityId).getParentId();
       Long receiverProvinceId = areaFeign.get(receiverCityId).getParentId();
       if(ObjectUtil.equals(sendProvinceId,receiverProvinceId)){
           CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_PROVINCE);
           if(ObjectUtil.isNotEmpty(carriageEntity)){
               return carriageEntity;
           }
       }
       //3.经济区互寄
      CarriageEntity carriageEntity= findByEconomic(sendProvinceId,receiverProvinceId);
      if(ObjectUtil.isNotEmpty(carriageEntity)){
          return carriageEntity;
      }

      //4.跨省
       carriageEntity = findByTemplateType(CarriageConstant.SAME_PROVINCE);
      if(ObjectUtil.isNotEmpty(carriageEntity)){
          return carriageEntity;
      }
      throw new SLException(CarriageExceptionEnum.NOT_FOUND);

   }

    private CarriageEntity findByEconomic(Long sendProvinceId, Long receiverProvinceId) {
       //KEY:BTH  VALUE:对象（code,[] value）
        LinkedHashMap<String, EconomicRegionEnum> enumMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        EconomicRegionEnum economicRegionEnum=null;
        for (EconomicRegionEnum regionEnum : enumMap.values()) {
            Long[] value = regionEnum.getValue();
            boolean containsAll= ArrayUtil.containsAll(value,sendProvinceId,receiverProvinceId);
            if(containsAll){
                economicRegionEnum=regionEnum;
                break;
            }
        }
        if(ObjectUtil.isNull(economicRegionEnum)){
            return null;
        }
        CarriageEntity carriageEntity = this.getOne(Wrappers.<CarriageEntity>lambdaQuery()
                .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
                .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode())
        );
        return carriageEntity;
    }


    @Override
    public CarriageEntity findByTemplateType(Integer templateType) {
       //判断经济区
        if(ObjectUtil.equals(CarriageConstant.ECONOMIC_ZONE,templateType)){
            throw new SLException(CarriageExceptionEnum.TEMPLAE_TYPE_NOT_SUPPORT);
        }
        //从redis里面获取缓存
        String redisKey = stringRedisTemplate.opsForValue().get(CARRIAGE_ENTITY_LIST);
        if(StrUtil.isNotBlank(redisKey)){
            List<CarriageEntity> carriageEntityList = JSONUtil.toList(redisKey, CarriageEntity.class);
            //使用stream流筛选
            Optional<CarriageEntity> carriageEntity = carriageEntityList.stream()
                    .filter(entity -> ObjectUtil.equals(entity.getTemplateType(), templateType))
                    .findFirst();
            if(carriageEntity.isPresent()){
                return carriageEntity.get();
            }
        }
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
                .eq(CarriageEntity::getTemplateType, templateType);
        CarriageEntity carriageEntity=this.getOne(queryWrapper);
        return carriageEntity;
    }
}
